/*
 *	dfsocket.c
 *	© 2009 DigiFi Limited
 *
 *	Entry point into the test harness for the libdigifi library
 *
 */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if_dl.h>
#include <net/if.h>


#include "../headers/dfsocket.h"


int dfsocket_connect(char *ipAddress, int port) {
	
	struct sockaddr_in *destination;
	int err;
	int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	
	// check if we failed to get a descriptor
	if (fd == -1)
		return -1;
	
	if ((destination = malloc(sizeof(struct sockaddr_in))) == NULL) {close(fd); return -DFSOCKET_NOMEM;}
	destination->sin_family = AF_INET;
	destination->sin_port = htons(port);

	err = inet_pton(AF_INET, ipAddress, &destination->sin_addr);

	// check if the address converstion fucked up
	if (err <= 0) {
		close(fd);
		free(destination);
		return -1;
	}
	
	err = connect(fd, (const void*)destination, sizeof(struct sockaddr_in));
	
	if (err == -1) {
		close(fd);
		free(destination);
		return -1;
	}
	
	free(destination);

	return fd;
}

int dfsocket_send(int id, const void *buffer, int length) {
	return send(id, buffer, length, 0);
}

int dfsocket_receive(int id, void *buffer, int length) {
	return recv(id, buffer, length, 0);
}

int dfsocket_poll(int id, int timeout, int type) {
	struct timeval tmo;
	fd_set fds;
	int err, return_val;
	
	tmo.tv_sec = timeout;
	
	FD_ZERO(&fds);
	FD_SET(id, &fds);
	
	if (type==1)		err = select(id+1,&fds,NULL,NULL,&tmo);
	else if (type==2)	err = select(id+1,NULL,&fds,NULL,&tmo);
	else				return_val = -1;
	
	if (err<0)			return_val = err;
	else if(err==0)		return_val = DFSOCKET_TIMEOUT;
	else				return_val = 0;
	
	
	return return_val;
}

void dfsocket_disconnect(int id) {
	
	shutdown(id, SHUT_RDWR);
	close(id);
}



char* dfsocket_getlocalip(void) {
	struct ifaddrs *ifap, *ifa;
	char *ret = NULL;
	
    if (getifaddrs(&ifap) != 0) {
		return NULL;
    }
	
    // cycle through available interfaces
    for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
        // Skip loopback, point-to-point and down interfaces
        if( ( ifa->ifa_flags & IFF_LOOPBACK ) || ( !( ifa->ifa_flags & IFF_UP ) ) ) {
            continue;
        }
        if( ifa->ifa_addr->sa_family == AF_INET ) {
            // We don't want the loopback interface. 
            if( ((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) {
                continue;
            }

	    ret = inet_ntoa(((struct sockaddr_in *) ifa->ifa_addr)->sin_addr);
            break;
        }
    }
    freeifaddrs(ifap);
	
    return ret;
	
}


int dfsocket_unbind(int id) {
	return 0;
}


int dfsocket_listen(int port) {
	
	struct sockaddr_in *destination;
	int err;
	int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	
	// check if we failed to get a descriptor
	if (fd == -1)
		return -1;
	
	if ((destination = malloc(sizeof(struct sockaddr_in))) == NULL) {close(fd); return -DFSOCKET_NOMEM;}
	destination->sin_family = AF_INET;
	destination->sin_port = htons(port);
	destination->sin_addr.s_addr=htonl(INADDR_ANY);

	/*err = inet_pton(AF_INET, "127.0.0.1", &destination->sin_addr);

	// check if the address converstion fucked up
	if (err <= 0) {
		close(fd);
		free(destination);
		return -2;
	}*/
	
	err = bind(fd, (const void*)destination, sizeof(struct sockaddr_in));

	if (err == -1) {
		close(fd);
		free(destination);
		return err;
	}
	
	free(destination);
	
	err = listen(fd, 1);
	
	if (err == -1) {
		close(fd);
		return err;
	}

	return fd;
	
}


int dfsocket_accept(int id) {
	return accept(id, NULL, 0);
}

int dfsocket_set_nonblocking(int id) {
	int flags;
	
	if ((flags = fcntl(id, F_GETFL, 0)) < 0) { return -1; } 
	if (fcntl(id, F_SETFL, flags | O_NONBLOCK) < 0) { return -2; } 
	
	return 0;
}




int dfsocket_set_blocking(int id) {
	int flags;
	
	if ((flags = fcntl(id, F_GETFL, 0)) < 0) { return -1; } 
	if (fcntl(id, F_SETFL, flags & (~O_NONBLOCK)) < 0) { return -2; }
	
	return 0;
	
}










